package com.lixinlei.security.api.filter;

import java.io.IOException;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import com.lixinlei.security.api.dao.UserRepository;
import com.lixinlei.security.api.entity.User;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.util.Base64Utils;
import org.springframework.web.filter.OncePerRequestFilter;

import com.lambdaworks.crypto.SCryptUtil;

@Component
@Order(2)
public class BasicAuthecationFilter extends OncePerRequestFilter {

    @Autowired
    private UserRepository userRepository;

    /**
     * Authorization 中携带的认证信息，是 username:password 经过 Base64 加密之后的值；
     * 该 Filter 验证的是，username:password 中的信息是否指向同一个用户；
     * @param request
     * @param response
     * @param filterChain
     * @throws ServletException
     * @throws IOException
     */
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
            throws ServletException, IOException {

        System.out.println(2);

        // 这里从 Authorization 中取出的，就直接是带了 Basic 开头的，经过 Base64 加密了的字符串
        String authHeader = request.getHeader("Authorization");
        if(StringUtils.isNotBlank(authHeader)) {

            String token64 = StringUtils.substringAfter(authHeader, "Basic ");
            String token = new String(Base64Utils.decodeFromString(token64));
            String[] items = StringUtils.splitByWholeSeparatorPreserveAllTokens(token, ":");

            String username = items[0];
            // 明文密码
            String password = items[1];

            User user = userRepository.findByUsername(username);

            // 看明文是否能变成加密串，check 方法是非常耗资源的，差不多 100ms
            if(user != null && SCryptUtil.check(password, user.getPassword())) {
                request.getSession().setAttribute("user", user.buildInfo());
                request.getSession().setAttribute("temp", "yes");
            }
        }

        // 除非登录过，否则每次访问 API 都要带上 Authorization 头
        try {
            // 即使认证没通过，请求也要放行到下一个安全机制：审计
            filterChain.doFilter(request, response);
        } finally {
            HttpSession session = request.getSession();
            if(session.getAttribute("temp") != null) {
                session.invalidate();
            }
        }
    }

}
